<template>
    <transition name="el-zoom-in-top" @after-leave="$emit('dodestroy')">
        <div
                v-show="visible"
                class="el-picker-panel el-date-range-picker el-popper"
                :class="[{
        'has-sidebar': $slots.sidebar || shortcuts,
        'has-time': showTime
      }, popperClass]">
            <div class="el-picker-panel__body-wrapper">
                <slot name="sidebar" class="el-picker-panel__sidebar"></slot>
                <div class="el-picker-panel__sidebar" v-if="shortcuts">
                    <button
                            type="button"
                            class="el-picker-panel__shortcut"
                            v-for="(shortcut, key) in shortcuts"
                            :key="key"
                            @click="handleShortcutClick(shortcut)">{{shortcut.text}}
                    </button>
                </div>
                <div class="el-picker-panel__body">
                    <div class="el-date-range-picker__time-header" v-if="showTime">
            <span class="el-date-range-picker__editors-wrap">
              <span class="el-date-range-picker__time-picker-wrap">
                <el-input
                        size="small"
                        :disabled="rangeState.selecting"
                        ref="minInput"
                        :placeholder="t('el.datepicker.startDate')"
                        class="el-date-range-picker__editor"
                        :value="minVisibleDate"
                        @input="val => handleDateInput(val, 'min')"
                        @change="val => handleDateChange(val, 'min')"/>
              </span>
              <span class="el-date-range-picker__time-picker-wrap" v-clickoutside="handleMinTimeClose">
                <el-input
                        size="small"
                        class="el-date-range-picker__editor"
                        :disabled="rangeState.selecting"
                        :placeholder="t('el.datepicker.startTime')"
                        :value="minVisibleTime"
                        @focus="minTimePickerVisible = true"
                        @input="val => handleTimeInput(val, 'min')"
                        @change="val => handleTimeChange(val, 'min')"/>
                <time-picker
                        ref="minTimePicker"
                        @pick="handleMinTimePick"
                        :time-arrow-control="arrowControl"
                        :visible="minTimePickerVisible"
                        @mounted="$refs.minTimePicker.format=timeFormat">
                </time-picker>
              </span>
            </span>
                        <span class="el-icon-arrow-right"></span>
                        <span class="el-date-range-picker__editors-wrap is-right">
              <span class="el-date-range-picker__time-picker-wrap">
                <el-input
                        size="small"
                        class="el-date-range-picker__editor"
                        :disabled="rangeState.selecting"
                        :placeholder="t('el.datepicker.endDate')"
                        :value="maxVisibleDate"
                        :readonly="!minDate"
                        @input="val => handleDateInput(val, 'max')"
                        @change="val => handleDateChange(val, 'max')"/>
              </span>
              <span class="el-date-range-picker__time-picker-wrap" v-clickoutside="handleMaxTimeClose">
                <el-input
                        size="small"
                        class="el-date-range-picker__editor"
                        :disabled="rangeState.selecting"
                        :placeholder="t('el.datepicker.endTime')"
                        :value="maxVisibleTime"
                        :readonly="!minDate"
                        @focus="minDate && (maxTimePickerVisible = true)"
                        @input="val => handleTimeInput(val, 'max')"
                        @change="val => handleTimeChange(val, 'max')"/>
                <time-picker
                        ref="maxTimePicker"
                        @pick="handleMaxTimePick"
                        :time-arrow-control="arrowControl"
                        :visible="maxTimePickerVisible"
                        @mounted="$refs.maxTimePicker.format=timeFormat">
                </time-picker>
              </span>
            </span>
                    </div>
                    <div class="el-picker-panel__content el-date-range-picker__content is-left">
                        <div class="el-date-range-picker__header">
                            <button
                                    type="button"
                                    @click="leftPrevYear"
                                    class="el-picker-panel__icon-btn el-icon-d-arrow-left"></button>
                            <button
                                    type="button"
                                    @click="leftPrevMonth"
                                    class="el-picker-panel__icon-btn el-icon-arrow-left"></button>
                            <button
                                    type="button"
                                    @click="leftNextYear"
                                    v-if="unlinkPanels"
                                    :disabled="!enableYearArrow"
                                    :class="{ 'is-disabled': !enableYearArrow }"
                                    class="el-picker-panel__icon-btn el-icon-d-arrow-right"></button>
                            <button
                                    type="button"
                                    @click="leftNextMonth"
                                    v-if="unlinkPanels"
                                    :disabled="!enableMonthArrow"
                                    :class="{ 'is-disabled': !enableMonthArrow }"
                                    class="el-picker-panel__icon-btn el-icon-arrow-right"></button>
                            <div>{{ leftLabel }}</div>
                        </div>
                        <date-table
                                selection-mode="range"
                                :date="leftDate"
                                :default-value="defaultValue"
                                :min-date="minDate"
                                :max-date="maxDate"
                                :range-state="rangeState"
                                :disabled-date="disabledDate"
                                :cell-class-name="cellClassName"
                                @changerange="handleChangeRange"
                                :first-day-of-week="firstDayOfWeek"
                                @pick="handleRangePick">
                        </date-table>
                    </div>
                    <div class="el-picker-panel__content el-date-range-picker__content is-right">
                        <div class="el-date-range-picker__header">
                            <button
                                    type="button"
                                    @click="rightPrevYear"
                                    v-if="unlinkPanels"
                                    :disabled="!enableYearArrow"
                                    :class="{ 'is-disabled': !enableYearArrow }"
                                    class="el-picker-panel__icon-btn el-icon-d-arrow-left"></button>
                            <button
                                    type="button"
                                    @click="rightPrevMonth"
                                    v-if="unlinkPanels"
                                    :disabled="!enableMonthArrow"
                                    :class="{ 'is-disabled': !enableMonthArrow }"
                                    class="el-picker-panel__icon-btn el-icon-arrow-left"></button>
                            <button
                                    type="button"
                                    @click="rightNextYear"
                                    class="el-picker-panel__icon-btn el-icon-d-arrow-right"></button>
                            <button
                                    type="button"
                                    @click="rightNextMonth"
                                    class="el-picker-panel__icon-btn el-icon-arrow-right"></button>
                            <div>{{ rightLabel }}</div>
                        </div>
                        <date-table
                                selection-mode="range"
                                :date="rightDate"
                                :default-value="defaultValue"
                                :min-date="minDate"
                                :max-date="maxDate"
                                :range-state="rangeState"
                                :disabled-date="disabledDate"
                                :cell-class-name="cellClassName"
                                @changerange="handleChangeRange"
                                :first-day-of-week="firstDayOfWeek"
                                @pick="handleRangePick">
                        </date-table>
                    </div>
                </div>
            </div>
            <div class="el-picker-panel__footer" v-if="showTime">
                <el-button
                        size="mini"
                        type="text"
                        class="el-picker-panel__link-btn"
                        @click="handleClear">
                    {{ t('el.datepicker.clear') }}
                </el-button>
                <el-button
                        plain
                        size="mini"
                        class="el-picker-panel__link-btn"
                        :disabled="btnDisabled"
                        @click="handleConfirm(false)">
                    {{ t('el.datepicker.confirm') }}
                </el-button>
            </div>
        </div>
    </transition>
</template>

<script type="text/babel">
    import {
        formatDate,
        parseDate,
        isDate,
        modifyDate,
        modifyTime,
        modifyWithTimeString,
        prevYear,
        nextYear,
        prevMonth,
        nextMonth,
        nextDate,
        extractDateFormat,
        extractTimeFormat
    } from 'element-ui/src/utils/date-util';
    import Clickoutside from 'element-ui/src/utils/clickoutside';
    import Locale from 'element-ui/src/mixins/locale';
    import TimePicker from './time';
    import DateTable from '../basic/date-table';
    import ElInput from 'element-ui/packages/input';
    import ElButton from 'element-ui/packages/button';

    const calcDefaultValue = (defaultValue) => {
        if (Array.isArray(defaultValue)) {
            return [new Date(defaultValue[0]), new Date(defaultValue[1])];
        } else if (defaultValue) {
            return [new Date(defaultValue), nextDate(new Date(defaultValue), 1)];
        } else {
            return [new Date(), nextDate(new Date(), 1)];
        }
    };

    export default {
        mixins: [Locale],

        directives: {Clickoutside},

        computed: {
            btnDisabled() {
                return !(this.minDate && this.maxDate && !this.selecting && this.isValidValue([this.minDate, this.maxDate]));
            },

            leftLabel() {
                return this.leftDate.getFullYear() + ' ' + this.t('el.datepicker.year') + ' ' + this.t(`el.datepicker.month${ this.leftDate.getMonth() + 1 }`);
            },

            rightLabel() {
                return this.rightDate.getFullYear() + ' ' + this.t('el.datepicker.year') + ' ' + this.t(`el.datepicker.month${ this.rightDate.getMonth() + 1 }`);
            },

            leftYear() {
                return this.leftDate.getFullYear();
            },

            leftMonth() {
                return this.leftDate.getMonth();
            },

            leftMonthDate() {
                return this.leftDate.getDate();
            },

            rightYear() {
                return this.rightDate.getFullYear();
            },

            rightMonth() {
                return this.rightDate.getMonth();
            },

            rightMonthDate() {
                return this.rightDate.getDate();
            },

            minVisibleDate() {
                if (this.dateUserInput.min !== null) return this.dateUserInput.min;
                if (this.minDate) return formatDate(this.minDate, this.dateFormat);
                return '';
            },

            maxVisibleDate() {
                if (this.dateUserInput.max !== null) return this.dateUserInput.max;
                if (this.maxDate || this.minDate) return formatDate(this.maxDate || this.minDate, this.dateFormat);
                return '';
            },

            minVisibleTime() {
                if (this.timeUserInput.min !== null) return this.timeUserInput.min;
                if (this.minDate) return formatDate(this.minDate, this.timeFormat);
                return '';
            },

            maxVisibleTime() {
                if (this.timeUserInput.max !== null) return this.timeUserInput.max;
                if (this.maxDate || this.minDate) return formatDate(this.maxDate || this.minDate, this.timeFormat);
                return '';
            },

            timeFormat() {
                if (this.format) {
                    return extractTimeFormat(this.format);
                } else {
                    return 'HH:mm:ss';
                }
            },

            dateFormat() {
                if (this.format) {
                    return extractDateFormat(this.format);
                } else {
                    return 'yyyy-MM-dd';
                }
            },

            enableMonthArrow() {
                const nextMonth = (this.leftMonth + 1) % 12;
                const yearOffset = this.leftMonth + 1 >= 12 ? 1 : 0;
                return this.unlinkPanels && new Date(this.leftYear + yearOffset, nextMonth) < new Date(this.rightYear, this.rightMonth);
            },

            enableYearArrow() {
                return this.unlinkPanels && this.rightYear * 12 + this.rightMonth - (this.leftYear * 12 + this.leftMonth + 1) >= 12;
            }
        },

        data() {
            return {
                popperClass: '',
                value: [],
                defaultValue: null,
                defaultTime: null,
                minDate: '',
                maxDate: '',
                leftDate: new Date(),
                rightDate: nextMonth(new Date()),
                rangeState: {
                    endDate: null,
                    selecting: false,
                    row: null,
                    column: null
                },
                showTime: false,
                shortcuts: '',
                visible: '',
                disabledDate: '',
                cellClassName: '',
                firstDayOfWeek: 7,
                minTimePickerVisible: false,
                maxTimePickerVisible: false,
                format: '',
                arrowControl: false,
                unlinkPanels: false,
                dateUserInput: {
                    min: null,
                    max: null
                },
                timeUserInput: {
                    min: null,
                    max: null
                }
            };
        },

        watch: {
            minDate(val) {
                this.dateUserInput.min = null;
                this.timeUserInput.min = null;
                this.$nextTick(() => {
                    if (this.$refs.maxTimePicker && this.maxDate && this.maxDate < this.minDate) {
                        const format = 'HH:mm:ss';
                        this.$refs.maxTimePicker.selectableRange = [
                            [
                                parseDate(formatDate(this.minDate, format), format),
                                parseDate('23:59:59', format)
                            ]
                        ];
                    }
                });
                if (val && this.$refs.minTimePicker) {
                    this.$refs.minTimePicker.date = val;
                    this.$refs.minTimePicker.value = val;
                }
            },

            maxDate(val) {
                this.dateUserInput.max = null;
                this.timeUserInput.max = null;
                if (val && this.$refs.maxTimePicker) {
                    this.$refs.maxTimePicker.date = val;
                    this.$refs.maxTimePicker.value = val;
                }
            },

            minTimePickerVisible(val) {
                if (val) {
                    this.$nextTick(() => {
                        this.$refs.minTimePicker.date = this.minDate;
                        this.$refs.minTimePicker.value = this.minDate;
                        this.$refs.minTimePicker.adjustSpinners();
                    });
                }
            },

            maxTimePickerVisible(val) {
                if (val) {
                    this.$nextTick(() => {
                        this.$refs.maxTimePicker.date = this.maxDate;
                        this.$refs.maxTimePicker.value = this.maxDate;
                        this.$refs.maxTimePicker.adjustSpinners();
                    });
                }
            },

            value(newVal) {
                if (!newVal) {
                    this.minDate = null;
                    this.maxDate = null;
                } else if (Array.isArray(newVal)) {
                    this.minDate = isDate(newVal[0]) ? new Date(newVal[0]) : null;
                    this.maxDate = isDate(newVal[1]) ? new Date(newVal[1]) : null;
                    if (this.minDate) {
                        this.leftDate = this.minDate;
                        if (this.unlinkPanels && this.maxDate) {
                            const minDateYear = this.minDate.getFullYear();
                            const minDateMonth = this.minDate.getMonth();
                            const maxDateYear = this.maxDate.getFullYear();
                            const maxDateMonth = this.maxDate.getMonth();
                            this.rightDate = minDateYear === maxDateYear && minDateMonth === maxDateMonth
                                ? nextMonth(this.maxDate)
                                : this.maxDate;
                        } else {
                            this.rightDate = nextMonth(this.leftDate);
                        }
                    } else {
                        this.leftDate = calcDefaultValue(this.defaultValue)[0];
                        this.rightDate = nextMonth(this.leftDate);
                    }
                }
            },

            defaultValue(val) {
                if (!Array.isArray(this.value)) {
                    const [left, right] = calcDefaultValue(val);
                    this.leftDate = left;
                    this.rightDate = val && val[1] && this.unlinkPanels
                        ? right
                        : nextMonth(this.leftDate);
                }
            }
        },

        methods: {
            handleClear() {
                this.minDate = null;
                this.maxDate = null;
                this.leftDate = calcDefaultValue(this.defaultValue)[0];
                this.rightDate = nextMonth(this.leftDate);
                this.$emit('pick', null);
            },

            handleChangeRange(val) {
                this.minDate = val.minDate;
                this.maxDate = val.maxDate;
                this.rangeState = val.rangeState;
            },

            handleDateInput(value, type) {
                this.dateUserInput[type] = value;
                if (value.length !== this.dateFormat.length) return;
                const parsedValue = parseDate(value, this.dateFormat);

                if (parsedValue) {
                    if (typeof this.disabledDate === 'function' &&
                        this.disabledDate(new Date(parsedValue))) {
                        return;
                    }
                    if (type === 'min') {
                        this.minDate = modifyDate(this.minDate || new Date(), parsedValue.getFullYear(), parsedValue.getMonth(), parsedValue.getDate());
                        this.leftDate = new Date(parsedValue);
                        if (!this.unlinkPanels) {
                            this.rightDate = nextMonth(this.leftDate);
                        }
                    } else {
                        this.maxDate = modifyDate(this.maxDate || new Date(), parsedValue.getFullYear(), parsedValue.getMonth(), parsedValue.getDate());
                        this.rightDate = new Date(parsedValue);
                        if (!this.unlinkPanels) {
                            this.leftDate = prevMonth(parsedValue);
                        }
                    }
                }
            },

            handleDateChange(value, type) {
                const parsedValue = parseDate(value, this.dateFormat);
                if (parsedValue) {
                    if (type === 'min') {
                        this.minDate = modifyDate(this.minDate, parsedValue.getFullYear(), parsedValue.getMonth(), parsedValue.getDate());
                        if (this.minDate > this.maxDate) {
                            this.maxDate = this.minDate;
                        }
                    } else {
                        this.maxDate = modifyDate(this.maxDate, parsedValue.getFullYear(), parsedValue.getMonth(), parsedValue.getDate());
                        if (this.maxDate < this.minDate) {
                            this.minDate = this.maxDate;
                        }
                    }
                }
            },

            handleTimeInput(value, type) {
                this.timeUserInput[type] = value;
                if (value.length !== this.timeFormat.length) return;
                const parsedValue = parseDate(value, this.timeFormat);

                if (parsedValue) {
                    if (type === 'min') {
                        this.minDate = modifyTime(this.minDate, parsedValue.getHours(), parsedValue.getMinutes(), parsedValue.getSeconds());
                        this.$nextTick(_ => this.$refs.minTimePicker.adjustSpinners());
                    } else {
                        this.maxDate = modifyTime(this.maxDate, parsedValue.getHours(), parsedValue.getMinutes(), parsedValue.getSeconds());
                        this.$nextTick(_ => this.$refs.maxTimePicker.adjustSpinners());
                    }
                }
            },

            handleTimeChange(value, type) {
                const parsedValue = parseDate(value, this.timeFormat);
                if (parsedValue) {
                    if (type === 'min') {
                        this.minDate = modifyTime(this.minDate, parsedValue.getHours(), parsedValue.getMinutes(), parsedValue.getSeconds());
                        if (this.minDate > this.maxDate) {
                            this.maxDate = this.minDate;
                        }
                        this.$refs.minTimePicker.value = this.minDate;
                        this.minTimePickerVisible = false;
                    } else {
                        this.maxDate = modifyTime(this.maxDate, parsedValue.getHours(), parsedValue.getMinutes(), parsedValue.getSeconds());
                        if (this.maxDate < this.minDate) {
                            this.minDate = this.maxDate;
                        }
                        this.$refs.maxTimePicker.value = this.minDate;
                        this.maxTimePickerVisible = false;
                    }
                }
            },

            handleRangePick(val, close = true) {
                const defaultTime = this.defaultTime || [];
                const minDate = modifyWithTimeString(val.minDate, defaultTime[0]);
                const maxDate = modifyWithTimeString(val.maxDate, defaultTime[1]);

                if (this.maxDate === maxDate && this.minDate === minDate) {
                    return;
                }
                this.onPick && this.onPick(val);
                this.maxDate = maxDate;
                this.minDate = minDate;

                // workaround for https://github.com/ElemeFE/element/issues/7539, should remove this block when we don't have to care about Chromium 55 - 57
                setTimeout(() => {
                    this.maxDate = maxDate;
                    this.minDate = minDate;
                }, 10);
                if (!close || this.showTime) return;
                this.handleConfirm();
            },

            handleShortcutClick(shortcut) {
                if (shortcut.onClick) {
                    shortcut.onClick(this);
                }
            },

            handleMinTimePick(value, visible, first) {
                this.minDate = this.minDate || new Date();
                if (value) {
                    this.minDate = modifyTime(this.minDate, value.getHours(), value.getMinutes(), value.getSeconds());
                }

                if (!first) {
                    this.minTimePickerVisible = visible;
                }

                if (!this.maxDate || this.maxDate && this.maxDate.getTime() < this.minDate.getTime()) {
                    this.maxDate = new Date(this.minDate);
                }
            },

            handleMinTimeClose() {
                this.minTimePickerVisible = false;
            },

            handleMaxTimePick(value, visible, first) {
                if (this.maxDate && value) {
                    this.maxDate = modifyTime(this.maxDate, value.getHours(), value.getMinutes(), value.getSeconds());
                }

                if (!first) {
                    this.maxTimePickerVisible = visible;
                }

                if (this.maxDate && this.minDate && this.minDate.getTime() > this.maxDate.getTime()) {
                    this.minDate = new Date(this.maxDate);
                }
            },

            handleMaxTimeClose() {
                this.maxTimePickerVisible = false;
            },

            // leftPrev*, rightNext* need to take care of `unlinkPanels`
            leftPrevYear() {
                this.leftDate = prevYear(this.leftDate);
                if (!this.unlinkPanels) {
                    this.rightDate = nextMonth(this.leftDate);
                }
            },

            leftPrevMonth() {
                this.leftDate = prevMonth(this.leftDate);
                if (!this.unlinkPanels) {
                    this.rightDate = nextMonth(this.leftDate);
                }
            },

            rightNextYear() {
                if (!this.unlinkPanels) {
                    this.leftDate = nextYear(this.leftDate);
                    this.rightDate = nextMonth(this.leftDate);
                } else {
                    this.rightDate = nextYear(this.rightDate);
                }
            },

            rightNextMonth() {
                if (!this.unlinkPanels) {
                    this.leftDate = nextMonth(this.leftDate);
                    this.rightDate = nextMonth(this.leftDate);
                } else {
                    this.rightDate = nextMonth(this.rightDate);
                }
            },

            // leftNext*, rightPrev* are called when `unlinkPanels` is true
            leftNextYear() {
                this.leftDate = nextYear(this.leftDate);
            },

            leftNextMonth() {
                this.leftDate = nextMonth(this.leftDate);
            },

            rightPrevYear() {
                this.rightDate = prevYear(this.rightDate);
            },

            rightPrevMonth() {
                this.rightDate = prevMonth(this.rightDate);
            },

            handleConfirm(visible = false) {
                if (this.isValidValue([this.minDate, this.maxDate])) {
                    this.$emit('pick', [this.minDate, this.maxDate], visible);
                }
            },

            isValidValue(value) {
                return Array.isArray(value) &&
                    value && value[0] && value[1] &&
                    isDate(value[0]) && isDate(value[1]) &&
                    value[0].getTime() <= value[1].getTime() && (
                        typeof this.disabledDate === 'function'
                            ? !this.disabledDate(value[0]) && !this.disabledDate(value[1])
                            : true
                    );
            },

            resetView() {
                // NOTE: this is a hack to reset {min, max}Date on picker open.
                // TODO: correct way of doing so is to refactor {min, max}Date to be dependent on value and internal selection state
                //       an alternative would be resetView whenever picker becomes visible, should also investigate date-panel's resetView
                if (this.minDate && this.maxDate == null) this.rangeState.selecting = false;
                this.minDate = this.value && isDate(this.value[0]) ? new Date(this.value[0]) : null;
                this.maxDate = this.value && isDate(this.value[0]) ? new Date(this.value[1]) : null;
            }
        },

        components: {TimePicker, DateTable, ElInput, ElButton}
    };
</script>
